package com.gitee.chili.leisure.util.encrypt;

import com.gitee.chili.leisure.exception.BusinessException;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * 组合加密算法
 */
public class CombinationCipher {

    /**
     * 秘钥
     * 经过组合加密后的字符串，解密后得到原始字符串 ("123456")
     */
    public static String key = "u03gZNz4dtIjRKQ9OwR2rsh2ciR7P1pX";

    /**
     * 加密方法
     *
     * @param xmlStr 需要加密的消息字符串
     * @return 加密后的字符串
     */
    public static String encrypt(String xmlStr) {
        byte[] encrypt = null;

        // 取需要加密内容的utf-8编码。
        encrypt = xmlStr.getBytes(StandardCharsets.UTF_8);
        // 取MD5Hash码，并组合加密数组
        byte[] md5Hash = null;
        try {
            md5Hash = MD5.MD5Hash(encrypt, 0, encrypt.length);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 组合消息体
        assert md5Hash != null;
        byte[] totalByte = MD5.addMD5(md5Hash, encrypt);

        byte[] temp = getDecryptOrEncryptByte(totalByte, "encrypt");

        // 使用Base64加密后返回
        return new BASE64Encoder().encode(temp);
    }

    /**
     * 解密方法
     *
     * @param xmlStr 需要解密的消息字符串
     * @return 解密后的字符串
     */
    public static String decrypt(String xmlStr) throws Exception {
        // base64解码
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] encBuf;
        try {
            encBuf = decoder.decodeBuffer(xmlStr);
        } catch (IOException e) {
            throw new BusinessException(e);
        }

        byte[] temp = getDecryptOrEncryptByte(encBuf, "decrypt");

        // 进行解密后的md5Hash校验
        byte[] md5Hash;
        try {
            md5Hash = MD5.MD5Hash(temp, 16, temp.length - 16);
        } catch (Exception e) {
            throw new BusinessException(e);
        }

        // 进行解密校检
        for (int i = 0; i < Objects.requireNonNull(md5Hash).length; i++) {
            if (md5Hash[i] != temp[i]) {
                throw new BusinessException("md5 check failure");
            }
        }

        // 返回解密后的数组，其中前16位MD5Hash码要除去。
        return new String(temp, 16, temp.length - 16, StandardCharsets.UTF_8);
    }

    private static byte[] getDecryptOrEncryptByte(byte[] encBuf, String type) {

        // 取密钥和偏转向量
        byte[] key = new byte[8];
        byte[] iv = new byte[8];
        getKeyIV(CombinationCipher.key, key, iv);

        SecretKeySpec desKey = new SecretKeySpec(key, "DES");
        IvParameterSpec ivParam = new IvParameterSpec(iv);

        try {
            // 使用DES算法解密
            return type.toLowerCase().equals("decrypt")
                    ? DES.decrypt(encBuf, desKey, ivParam)
                    : DES.encrypt(encBuf, desKey, ivParam);
        } catch (Exception e) {
            throw new BusinessException(e);
        }

    }

    public static void getKeyIV(String encryptKey, byte[] key, byte[] iv) {
        // 密钥Base64解密  
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] buf = null;
        try {
            buf = decoder.decodeBuffer(encryptKey);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 前8位为key  
        int i;
        for (i = 0; i < key.length; i++) {
            assert buf != null;
            key[i] = buf[i];
        }
        // 后8位为iv向量  
        for (i = 0; i < iv.length; i++) {
            assert buf != null;
            iv[i] = buf[i + 8];
        }
    }

    public static void main(String[] args) {
        System.out.println(encrypt("123456"));
    }
}
